#include "GameOpt.h"
#include "Monster.h"
#include "Entity.h"
#include "Maze.h"


Monster::Monster( const char inToken, Point DesPoint,
				unsigned int MaxMovement, unsigned int MaxHealth, 
				unsigned int Defense, unsigned int Power,
				unsigned int Movement, unsigned int Health,
				double Visibility, double Brains)
:Entity(inToken, DesPoint, MaxMovement, MaxHealth,
		Defense, Power, Movement, Health),dVisibility(Visibility),dBrains(Brains)
{
#ifdef DBG
	uiMaxMovement=uiMovement=10;
	uiMaxHealth=uiHealth=100;
	uiDefense=10;
	uiPower=10;
	dVisibility=10;
	dBrains=1;

#else
	int Rand1 = RandomInt(1,10); // Monster randomizer
	int Rand2 = RandomInt(1,6);

	uiMaxMovement+=PERESENT_TYPE_MOVMENT_MAX_I*10;
	uiMaxHealth+=PERESENT_TYPE_HEALTH_I;
	uiDefense+=PERESENT_TYPE_DEFENCE_I;
	uiPower+=PERESENT_TYPE_POWER_I;

	dVisibility = Rand1;
	dBrains = 1/Rand2;

	switch(Rand2)
	{
	case 1:
		uiMaxMovement+=Rand1;
		break;
	case 2:
		uiMaxHealth+=Rand1;
		break;
	case 3:
		uiDefense+=Rand1;
		break;
	case 4:
		uiPower+=Rand1;
		break;
	}

	uiMovement = uiMaxMovement;
	uiHealth= uiMaxHealth;
#endif
}

Monster::~Monster()
{
//	int i=0;

//	if (Vrange)
//	for (i=0;i<MAZE_SIZE_X;i++)			// delete Vrange[][] if exists
//		delete [] Vrange[i];

}

char Monster::kind() const
{
	return KIND_MONSTER;
}

int Monster::initPath(Maze & GameMaze)
{
	if (!Path.empty())
		return 0;

	vector <Point> vRoute;
	vector<stack<char>> TmpPaths;

	//get a smart path for the monster
	getVrange(GameMaze);
	Paths.clear();

	TmpPaths.clear();
	Search('U',vRoute,Pos,Vrange,GameMaze,TmpPaths);
	if (TmpPaths.size())
	{
		sort(TmpPaths.begin(),TmpPaths.end(),StackCompare()); // sort Paths in ascending order
		Paths=TmpPaths;
	}

	TmpPaths.clear();
	Search('R',vRoute,Pos,Vrange,GameMaze,TmpPaths);
	if (TmpPaths.size())
	{
		sort(TmpPaths.begin(),TmpPaths.end(),StackCompare()); // sort Paths in ascending order
		if (TmpPaths[0].size()<Paths[0].size())
			Paths=TmpPaths;
	}

	TmpPaths.clear();
	Search('D',vRoute,Pos,Vrange,GameMaze,TmpPaths);
	if (TmpPaths.size())
	{
		sort(TmpPaths.begin(),TmpPaths.end(),StackCompare()); // sort Paths in ascending order
		if (TmpPaths[0].size()<Paths[0].size())
			Paths=TmpPaths;
	}

	TmpPaths.clear();
	Search('L',vRoute,Pos,Vrange,GameMaze,TmpPaths);
	if (TmpPaths.size())
	{
		sort(TmpPaths.begin(),TmpPaths.end(),StackCompare()); // sort Paths in ascending order
		if (TmpPaths[0].size()<Paths[0].size())
			Paths=TmpPaths;
	}

	if (Paths.size())
	{
		int i=(int)((1-dBrains)*Paths.size()); // if dBrains==1, the shortest path is chosen
		Path=Paths[i];
		return 1; // smart path was found
	}
	else
	{
		while (!Path.empty())
		Path.pop(); // clear path
		vRoute.clear(); // clear route
		vRoute.push_back(Pos);
		unsigned int randomNum;
		Point DesPos;
		// get a random path for a monser incase no smart path was found
		while (vRoute.size()<=uiMovement)
		{
			randomNum=RandomInt(1,5);
			switch (randomNum)
			{
			case 1: // UP
				DesPos=vRoute.back()-Point(0,1); //UP
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()+Point(1,0); //RIGHT
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()+Point(0,1); //DOWN
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()-Point(1,0); //LEFT
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				vRoute.push_back(vRoute.back()); // STAY
				break;
			case 2: // RIGHT
				DesPos=vRoute.back()+Point(1,0); //RIGHT
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()+Point(0,1); //DOWN
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()-Point(1,0); //LEFT
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()-Point(0,1); //UP
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				vRoute.push_back(vRoute.back()); // STAY
				break;
			case 3: // DOWN
				DesPos=vRoute.back()+Point(0,1); //DOWN
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()-Point(1,0); //LEFT
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()-Point(0,1); //UP
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()+Point(1,0); //RIGHT
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				vRoute.push_back(vRoute.back()); // STAY
				break;
			case 4: // LEFT
				DesPos=vRoute.back()-Point(1,0); //LEFT
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()-Point(0,1); //UP
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()+Point(1,0); //RIGHT
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				DesPos=vRoute.back()+Point(0,1); //DOWN
				if (!GameMaze(DesPos).isWall() && !wasHere(vRoute,DesPos))
				{
					vRoute.push_back(DesPos);
					break;
				}
				vRoute.push_back(vRoute.back()); // STAY
				break;
			case 5: // STAY
				vRoute.push_back(vRoute.back());
				break;
			default:
				vRoute.push_back(vRoute.back());
				break;
			}
		}
		Path=Route2Path(vRoute);
		return 2; // random path
	}
}
Point Monster::reqMove(Maze & GameMaze)
{
	if ((uiMovement == uiMaxMovement) || // turn start
		(Path.empty() && uiMovement != 0)) // middle of turn
		initPath(GameMaze);

	Point pntNextPos;

	char cArrowKey='N';
	cArrowKey=Path.top();
	Path.pop();

	int x=Pos.x(); int y=Pos.y();
	switch (cArrowKey)
	{
	case 'U':
		y=y-1;
		break;
	case 'D':
		y=y+1;
		break;
	case 'L':
		x=x-1;
		break;
	case 'R':
		x=x+1;
		break;
	default:
		break;
	}

	pntNextPos(x,y);

	return pntNextPos;
}

bool Monster::setVisibility(double dVis)
{
	dVisibility=dVis;
	return true;
}
int Monster::StrikeOut(Point & Pos,const int x, const int y) // Strike out visible range, returns number of changes
{
	int changes=0;
	int i=0,j=0;
	Point diff=Point(x,y)-Pos;
	int Quadrate=diff.getQuadrate();
	double maxAng=0; // maxsimum angle not visible
	double minAng=0; // minimum angle not visible

	switch (Quadrate)
	{
	case 1:
		maxAng=Pos.angle(Point(x-1,y));
		minAng=Pos.angle(Point(x,y+1));

		for (j=y-1,i=x;i<MAZE_SIZE_X-1 && j>0;j--)
			for(i=x;i<MAZE_SIZE_X-1;i++)
				if (Vrange[i][j] && !IsVisible(Pos,minAng,maxAng,Point(i,j)))
				{
					Vrange[i][j]=false;
					changes++;
					changes+=StrikeOut(Pos,i,j);
				}
		break;

	case 2:
		minAng=Pos.angle(Point(x+1,y));
		maxAng=Pos.angle(Point(x,y+1));

		for (j=y-1,i=x;i>0 && j>0;j--)
			for(i=x;i>0;i--)
				if (Vrange[i][j] && !IsVisible(Pos,minAng,maxAng,Point(i,j)))
				{
					Vrange[i][j]=false;
					changes++;
					changes+=StrikeOut(Pos,i,j);
				}
		break;

	case 3:
		minAng=Pos.angle(Point(x,y-1));
		maxAng=Pos.angle(Point(x+1,y));

		for (j=y+1,i=x;i>0 && j<MAZE_SIZE_Y-1;j++)
			for(i=x;i>0;i--)
				if (Vrange[i][j] && !IsVisible(Pos,minAng,maxAng,Point(i,j)))
				{
					Vrange[i][j]=false;
					changes++;
					changes+=StrikeOut(Pos,i,j);
				}
		break;

	case 4:
		minAng=Pos.angle(Point(x-1,y));
		maxAng=Pos.angle(Point(x,y-1));

		for (j=y+1,i=x;i<MAZE_SIZE_X-1 && j<MAZE_SIZE_Y-1;j++)
			for(i=x;i<MAZE_SIZE_X-1;i++)
				if (Vrange[i][j] && !IsVisible(Pos,minAng,maxAng,Point(i,j)))
				{
					Vrange[i][j]=false;
					changes++;
					changes+=StrikeOut(Pos,i,j);
				}
		break;

	case 0:		// right
		for(i=x+1,j=y;i<MAZE_SIZE_Y;i++)
			if (Vrange[i][j])
			{
				Vrange[i][j]=false;
				changes++;
			}
		break;

	case 90:	// upwards
		for(i=x,j=y-1;j>=0;j--)
			if (Vrange[i][j])
			{
				Vrange[i][j]=false;
				changes++;
			}
		break;

	case 180:	// left
		for(i=x-1,j=y;i>=0;i--)
			if (Vrange[i][j])
			{
				Vrange[i][j]=false;
				changes++;
			}
		break;

	case 270:	// downwards
		for(i=x,j=y+1;j<MAZE_SIZE_Y;j++)
			if (Vrange[i][j])
			{
				Vrange[i][j]=false;
				changes++;
			}
		break;

	default:
		return -1;	// error
		break;
	}

	return changes;
}
int Monster::getVrange(Maze & GameMaze)
{
	int i=0,j=0;
	int changes=0;

	for (i=0;i<MAZE_SIZE_X;i++)
		for (j=0;j<MAZE_SIZE_Y;j++)
			Vrange[i][j]=true;			//initialize range

	for (i=0;i<MAZE_SIZE_X;i++)
		for (j=0;j<MAZE_SIZE_Y;j++)
			if ((Pos%Point(i,j)>dVisibility) && (Vrange[i][j]))
				Vrange[i][j]=false;
			else
			{
				if (GameMaze(i,j).isWall())
				{
					Vrange[i][j]=false;
					changes+=StrikeOut(Pos,i,j);
				}
				else
					if (GameMaze(i,j).isMonster() && !(Pos==Point(i,j)))
						Vrange[i][j]=false;
			}
	return changes;
}

bool Monster::IsVisible(Point & Pos,const double minAng,const double maxAng,Point Target) const
{
	double ang=Pos.angle(Target);
	Point diff=Target-Pos;
	int Quadrate=diff.getQuadrate();
//	int x=Target.x();
//	int y=Target.y();

	if (Quadrate>=1 && Quadrate <= 4 && // inside the quadrate
		ang >= minAng && 
		ang <= maxAng)
		
	{
		return false;
	}
	
	else
		if (Quadrate<1 || Quadrate>4 && // inbetween quadrates			
			ang > minAng && 
			ang < maxAng)
			{
				return false;
			}
	return true;
}

bool Monster::wasHere(vector<Point> & vec, Point& pnt)
{
	unsigned int i=0;

	for(i=0 ; i < vec.size() ; i++)
	{
		if (vec[i]==pnt)
			return true;
	}
	return false;
}
int Monster::Search(char Dir,vector<Point> vRoute ,Point dPos,const bool Vrange[50][24],Maze & GameMaze, vector<stack<char>> & Paths)
{
	static int runs=0;
	static Point vMov(0,1); // vertical movement
	static Point hMov(1,0); // horizontal movement


	int x=dPos.x(); int y=dPos.y();

	if (vRoute.size()>uiMovement) //cannot make a long route
		return 0;

	if (wasHere(vRoute,dPos)) // cannot step on the same position
	{
		return 0;
	}

	if (Paths.size()>MAX_PATHS-1) // no more then MAX_PATHS paths
		return 0;

	if (!Vrange[x][y])		 // in visible range?
	{
		return 0;
	}
	if (GameMaze(x,y).isHuman())
	{
		vRoute.push_back(dPos);
		Paths.push_back(Route2Path(vRoute));
		return 1;
	}
	
	vRoute.push_back(dPos);
	int U,R,L,D;
	switch(Dir)
	{
	case 'U':
		//UP
		U=Search(Dir, vRoute, dPos-vMov,Vrange,GameMaze,Paths);
		//Right
		R=Search(Dir, vRoute, dPos+hMov,Vrange,GameMaze,Paths);
		//Down
		D=Search(Dir, vRoute, dPos+vMov,Vrange,GameMaze,Paths);
		//Left
		L=Search(Dir, vRoute, dPos-hMov,Vrange,GameMaze,Paths);
		break;
	case 'L':
		//Left
		L=Search(Dir, vRoute, dPos-Point(1,0),Vrange,GameMaze,Paths);
		//UP
		U=Search(Dir, vRoute, dPos-Point(0,1),Vrange,GameMaze,Paths);
		//Right
		R=Search(Dir, vRoute, dPos+Point(1,0),Vrange,GameMaze,Paths);
		//Down
		D=Search(Dir, vRoute, dPos+Point(0,1),Vrange,GameMaze,Paths);
		break;
	case 'D':
		//Down
		D=Search(Dir, vRoute, dPos+Point(0,1),Vrange,GameMaze,Paths);
		//Left
		L=Search(Dir, vRoute, dPos-Point(1,0),Vrange,GameMaze,Paths);
		//UP
		U=Search(Dir, vRoute, dPos-Point(0,1),Vrange,GameMaze,Paths);
		//Right
		R=Search(Dir, vRoute, dPos+Point(1,0),Vrange,GameMaze,Paths);
		break;
	case 'R':
		//Right
		R=Search(Dir, vRoute, dPos+Point(1,0),Vrange,GameMaze,Paths);
		//Down
		D=Search(Dir, vRoute, dPos+Point(0,1),Vrange,GameMaze,Paths);
		//Left
		L=Search(Dir, vRoute, dPos-Point(1,0),Vrange,GameMaze,Paths);
		//UP
		U=Search(Dir, vRoute, dPos-Point(0,1),Vrange,GameMaze,Paths);
		break;
	default:
		//UP
		U=Search(Dir, vRoute, dPos-Point(0,1),Vrange,GameMaze,Paths);
		//Right
		R=Search(Dir, vRoute, dPos+Point(1,0),Vrange,GameMaze,Paths);
		//Down
		D=Search(Dir, vRoute, dPos+Point(0,1),Vrange,GameMaze,Paths);
		//Left
		L=Search(Dir, vRoute, dPos-Point(1,0),Vrange,GameMaze,Paths);
		break;
	}
	
	return 0;
}

stack <char> Monster::Route2Path(vector<Point> & vec)
{
	stack <char> stk;
	unsigned int i=0;
	Point diff;


	for (i=vec.size()-1 ; i > 0 ; i--)
	{
		diff = vec[i] - vec[i-1];
		if (diff == Point(0,-1))
			stk.push('U');
		else
			if (diff == Point(1,0))
				stk.push('R');
			else
				if (diff == Point(0,1))
					stk.push('D');
				else
					if (diff == Point(-1,0))
						stk.push('L');
	}
	return stk;
}

bool Monster::SaveState(ofstream & FileName)
{
	FileName.write( reinterpret_cast<char*>(&uiPower), sizeof(uiPower));
	FileName.write( reinterpret_cast<char*>(&uiDefense), sizeof(uiDefense));
	FileName.write( reinterpret_cast<char*>(&uiHealth), sizeof(uiHealth));
	FileName.write( reinterpret_cast<char*>(&uiMaxHealth), sizeof(uiMaxHealth));
	FileName.write( reinterpret_cast<char*>(&uiMovement), sizeof(uiMovement));
	FileName.write( reinterpret_cast<char*>(&uiMaxMovement), sizeof(uiMaxMovement));
	FileName.write( reinterpret_cast<char*>(&cToken), sizeof(cToken));
	FileName.write( reinterpret_cast<char*>(&Pos), sizeof(Pos));

	FileName.write( reinterpret_cast<char*>(&dVisibility), sizeof(dVisibility));
	FileName.write( reinterpret_cast<char*>(&dBrains), sizeof(dBrains));
	return 1;
}

bool Monster::LoadState(ifstream & FileName)
{
	FileName.read( reinterpret_cast<char*>(&uiPower), sizeof(uiPower));
	FileName.read( reinterpret_cast<char*>(&uiDefense), sizeof(uiDefense));
	FileName.read( reinterpret_cast<char*>(&uiHealth), sizeof(uiHealth));
	FileName.read( reinterpret_cast<char*>(&uiMaxHealth), sizeof(uiMaxHealth));
	FileName.read( reinterpret_cast<char*>(&uiMovement), sizeof(uiMovement));
	FileName.read( reinterpret_cast<char*>(&uiMaxMovement), sizeof(uiMaxMovement));
	FileName.read( reinterpret_cast<char*>(&cToken), sizeof(cToken));
	FileName.read( reinterpret_cast<char*>(&Pos), sizeof(Pos));

	FileName.read( reinterpret_cast<char*>(&dVisibility), sizeof(dVisibility));
	FileName.read( reinterpret_cast<char*>(&dBrains), sizeof(dBrains));

	Paths.clear();
	while (!Path.empty())
		Path.pop();
	return 1;
}